/*
  Copyright (C) 2011, Kyungjoo Kim
  
  This file is part of LINAL (LINear ALgebra)
  
  All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.
  
  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.
  
  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  Also add information on how to contact you by electronic and paper mail.
  
  
  Kyungjoo Kim
  iamkyungjoo@gmail.com
*/

#include "linal/common.hxx"
#include "linal/util.hxx"

#include "linal/format.hxx"

#include "linal/operation.hxx"

#include "linal/device/operation.hxx"
#include "linal/device/device.hxx"
#include "linal/dispatcher.hxx"
  
#define LINAL_DISPATCHER_HERK_HIER                                      \
  {                                                                     \
    dev->inherit();                                                     \
    Hier_ HA, HC;                                                       \
    HA.create( Flat_(A), b_mn, b_mn );                                  \
    HC.create( Flat_(C), b_mn, b_mn );                                  \
                                                                        \
    r_val = linal::herk( uplo, trans, alpha, HA, beta, HC );            \
    invalidate(id, dev, C);                                             \
  }
  
#define LINAL_DISPATCHER_HERK_DEV				\
  {								\
    group->acquire_device();                                    \
    dev->lock();						\
    r_val = dev->herk( uplo, trans, alpha, A, beta, C );	\
    dev->unlock();						\
    group->release_device();                                    \
                                                                \
    if ( r_val == LINAL_DEVICE_RETURN_NOT_SUPPORT ) {           \
      Dispatcher_::Group_* child = get_group(--id);               \
      int b_mn = child->get_blocksize();                        \
      LINAL_DISPATCHER_HERK_HIER;                               \
    }                                                           \
  }

namespace linal {

  int Dispatcher_::herk( int uplo, int trans,
                         FLA_Obj &alpha,  FLA_Obj &A,
                         FLA_Obj &beta,   FLA_Obj &C ) {

    int                  r_val = LINAL_DEVICE_RETURN_NOT_SUPPORT;
    int                  id    = get_group( C );     // select group for C
    Dispatcher_::Group_* group = get_group( id );    // get group object
    Device               dev   = get_device( id );   // get device object

    if ( id ) {
      switch (get_computing_model()) {
      case LINAL_COM_HOST:
        {
          Dispatcher_::Group_* host = get_group(0);
          int b_mn = host->get_blocksize();
          LINAL_DISPATCHER_HERK_HIER;
        }
        break;
      case LINAL_COM_DEVICE:
        {
          LINAL_DISPATCHER_HERK_DEV;
        }
        break;
      case LINAL_COM_CASCADE:
        {
          if ( dev->is_inheritable() ) {
            if ( get_group(--id)->is_avail() ) {
              int b_mn = get_group(id)->get_blocksize();
              LINAL_DISPATCHER_HERK_HIER;
            } else {
              ++id;
              LINAL_DISPATCHER_HERK_DEV;
            }
          } else {
            LINAL_DISPATCHER_HERK_DEV;
          }
        }
        break;
      }
    } else {
      // host 
      LINAL_DISPATCHER_HERK_DEV;
    }
    return r_val;
  }
}
